package cn.lic.tool.poi.excel.function;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import cn.lic.tool.poi.excel.MappdServiceLoader;
import cn.lic.tool.poi.excel.POILoggerUtil;

public class FunctionParser {
	
	/**
	 * 圆括号（左）
	 */
	private final static char LEFT_BRACKET = '(';
	/**
	 * 圆括号（右）
	 */
	private final static char RIGHT_BRACKET = ')';
	
	/**
	 * 参数分隔符
	 */
	private final static char COMMA = ',';
	
	/**
	 * 变量函数别名
	 */
	private final static String VAR_FUN_ALIAS = "V";
	
	/**
	 * 转义字符
	 */
	private static Map<String, Character> REVERSE_CHARS = initReverseChars();
	
	private final static MappdServiceLoader<IFunction> funService = MappdServiceLoader.load(IFunction.class, FunctionParser.class.getClassLoader());
	
	public static IFunction getFunction(String express0) {
		if (FunctionParser.isVariable(express0)) {
			String express = express0.substring(2, express0.length() - 1);
			if (isFunction(express)) {
				// 解析函数
				return parseFunction(express);
			} else {
				// 解析变量
				POILoggerUtil.info("解析变量:" + express0);
				IFunction ins = Objects.requireNonNull(funService.getService(VAR_FUN_ALIAS), String.format("无法解析变量表达式%1s， 函数不存在%2s", express0, VAR_FUN_ALIAS) );
				express = unescape(express);
				return ins.build(new IFunctionArgument[]{ IValue.parse(express) });
			}
		} else {
			return null;
		}
	}
	private static IFunction parseFunction(String functionExpress) {
			
			// 直接量
			if (!isFunction(functionExpress)) {
				IFunction ins = Objects.requireNonNull(funService.getService(VAR_FUN_ALIAS), String.format("无法解析直接量表达式%1s， 函数不存在%2s", functionExpress, VAR_FUN_ALIAS) );
				functionExpress = unescape(functionExpress);
				return ins.build(new IValue[] {  IValue.parse(functionExpress) });
			}
			
			// 函数名
			String funName = null;
			String funExpressArgs = null;
			
			try {
				funName = functionExpress.substring(0, functionExpress.indexOf(LEFT_BRACKET)).trim();
				funExpressArgs = functionExpress.substring(functionExpress.indexOf(LEFT_BRACKET) + 1, functionExpress.lastIndexOf(RIGHT_BRACKET));
			} catch (Exception e) {
				throw new FunctionException("不是有效的函数表达式：" + functionExpress);
			}
			
			char[] chars = funExpressArgs.toCharArray();
			
			// 当前指针是否在括号内
			int inBracket = 0;
			
			// 下一个tocken的开始位置
			int offset = 0;
			int end = chars.length;
			
			// 解析参数列表
			List<IFunctionArgument> args = new ArrayList<>();
			
			for (int i = 0; i < end; i++) {
				switch (chars[i]) {
					case COMMA: 
						if (inBracket == 0) {
							String tocken = funExpressArgs.substring(offset, i).trim();
							args.add(parseFunction(tocken));
							offset = i+1;
						}
						break;
						
					case LEFT_BRACKET:
						inBracket++;
						break;
						
					case RIGHT_BRACKET:
						inBracket--;
						break;
				}
			}
			
			if (inBracket != 0) {
				throw new FunctionException("无法解析函数表达式, 嵌套函数有误：" + functionExpress);
			} else if (offset < end) {
				String tocken = funExpressArgs.substring(offset, end).trim();
				args.add(parseFunction(tocken));
			}
			
			if (!funService.containsService(funName)) {
				throw new FunctionException("不支持的函数名称：" + funName);
			}
			IFunction ins = Objects.requireNonNull(funService.getService(funName), String.format("无法解析表达式%1s， 函数不存在%2s", functionExpress, funName) );
			ins = ins.build(args.toArray(new IFunctionArgument[args.size()]));
			return ins;
	}
	
	/**
	 * 判断字符串是否是变量表达式, ${...}形式
	 * @return
	 */
	public static boolean isVariable(String pattern) {
		if (pattern.startsWith("${") && pattern.endsWith("}")) {
			return pattern.indexOf("${", 2) < 0;
		}
		return false;
	}
	
	/**
	 * 判断字符串是否是模板函数定义
	 * @param pattern
	 * @return
	 */
	public static boolean isFunction(String pattern) {
		if (pattern == null || pattern.length() < 2) {
			return false;
		}
		return pattern.indexOf(LEFT_BRACKET) >= 1 && pattern.charAt(pattern.length() - 1) == RIGHT_BRACKET;
	}
	
	/**
	 * 解析转义字符
	 * @param str
	 * @return
	 */
	public static String unescape(String str) {
		if (str == null) {
			return null;
		}
		int firstAmp = str.indexOf('&');
		if (firstAmp < 0) {
			return str;
		}
		
		 int len = str.length();
		 StringBuffer writer = new StringBuffer();
		 writer.append(str, 0, firstAmp);
		for (int i = firstAmp; i < len; i++) {
            char c = str.charAt(i);
            if (c == '&') {
                int nextIdx = i + 1;
                int semiColonIdx = str.indexOf(';', nextIdx);
                if (semiColonIdx == -1) {
                    writer.append(c);
                    continue;
                }
               
                String tocken = str.substring(i, semiColonIdx + 1);
                if (tocken.length() > 0) {
                	Character ct = REVERSE_CHARS.get(tocken);
                	if (ct == null) {
                		writer.append(tocken);
                	} else {
                		writer.append(ct);
                	}
                } else {
                	writer.append(str.substring(i, semiColonIdx + 1));
                }
                i = semiColonIdx;
            } else {
                writer.append(c);
            }
        }
		
		return writer.toString();
	}
	
	private static Map<String, Character> initReverseChars() {
		Map<String, Character> map = new HashMap<>();
		map.put("&lb;", '('); // 左圆括弧
		map.put("&rb;", ')'); // 右圆括弧
		map.put("&qt;", '\'');  // 单引号
		map.put("&dt;", '"'); // 双引号
		map.put("&cm;", ','); // 逗号
		map.put("&amp;", '&'); // &, 与运算符
		return map;
	}
	
//	/**
//	 * 执行模板函数
//	 * @param funPattern
//	 * @param context
//	 * @return
//	 */
//	public static BigDecimal execute(String funPattern, JSONObject context) {
//		String funName = funPattern.substring(0, funPattern.indexOf('('));
//		if (funs.containsKey(funName)) {
//			return funs.get(funName).execute().getResult();
//		} else {
//			throw new FunctionException("不支持的模板函数:" + funName);
//		}
//	}
}
